home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / kernel / ui.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  49.3 KB  |  1,894 lines  |  [TEXT/R*ch]

  1. /* Graphics support not specific to any Xconq interface.
  2.    Copyright (C) 1993, 1994, 1995, 1996 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. /* This file includes some very general graphics-related functionality
  10.    that interfaces can (but are not required to) use.  For instance,
  11.    the size and shapes of hex cells have been precalculated to provide
  12.    a reasonable appearance at several magnifications.  Note that some
  13.    of the algorithms in this file are abstracted from code that has been
  14.    tuned and tweaked over many years, so it is strongly recommended that
  15.    all new graphical interfaces use these. */
  16.  
  17. #include "conq.h"
  18. extern int disband_unit_directly PARAMS ((Side *side, Unit *unit));
  19. #include "imf.h"
  20. extern void make_generic_image_data PARAMS ((ImageFamily *imf));
  21. #include "ui.h"
  22.  
  23. #ifdef MAC /* temporary */
  24. #include <Types.h>
  25. #include <Resources.h>
  26. #endif
  27.  
  28. static void compute_q PARAMS ((void));
  29.  
  30. static int blocking_utype PARAMS ((int u, int block));
  31.  
  32. static ImageFamily *get_generic_images PARAMS ((Side *side, char *name, 
  33.                           void (*interp_hook)(ImageFamily *imf),
  34.                           void (*load_hook)(ImageFamily *imf)));
  35.  
  36. /* The two games that should be always be available. */
  37.  
  38. char *first_game_name = "intro";
  39.  
  40. char *second_game_name = "standard";
  41.  
  42. /* The following magical arrays set all the sizes at each magnification. */
  43.  
  44. /* This is the basic cell size. */
  45.  
  46. short mags[NUMPOWERS] = { 1, 2, 4, 8, 16, 32, 64, 128 };
  47.  
  48. /* These give the total dimensions of each hex cell, plus the vertical
  49.    distance center-to-center.  This is all carefully calculated to make
  50.    the cells overlap perfectly at each different magnification, assuming
  51.    that the icons have the right shape and size. */
  52.  
  53. short hws[NUMPOWERS] = { 1, 2, 4, 8, 24, 44, 88, 174 };
  54. short hhs[NUMPOWERS] = { 1, 2, 4, 8, 26, 48, 96, 192 };
  55. short hcs[NUMPOWERS] = { 1, 2, 4, 8, 20, 37, 75, 148 };
  56.  
  57. /* The sizes of the unit subcells.  This is available drawing area, exact
  58.    unit icon sizes depends on what's available. */
  59.  
  60. short uhs[NUMPOWERS] = { 1, 2, 3, 8, 16, 32, 64, 128 };
  61. short uws[NUMPOWERS] = { 1, 2, 3, 8, 16, 32, 64, 128 };
  62.  
  63. /* Widths of borders and connections (0 implies don't draw at all). */
  64.  
  65. /* Full border width. */
  66.  
  67. short bwid[NUMPOWERS] = { 0, 0, 1, 1, 3, 5, 7, 9 };
  68.  
  69. /* Half-width, for narrower inset borders. */
  70.  
  71. short bwid2[NUMPOWERS] = { 0, 0, 1, 1, 2, 3, 4, 5 };
  72.  
  73. /* Full connection width. */
  74.  
  75. short cwid[NUMPOWERS] = { 0, 0, 1, 1, 3, 5, 7, 9 };
  76.  
  77. /* Coordinates of the hex borders. */
  78. /* Note that array has extra column so don't need to wrap index. */
  79.  
  80. short bsx[NUMPOWERS][7] = {
  81.     { 0 },
  82.     { 0 },
  83.     {  2,   4,   4,  2,  0,  0,  2 },
  84.     {  4,   8,   8,  4,  0,  0,  4 },
  85.     { 12,  24,  24, 12,  0,  0, 12 },
  86.     { 22,  44,  44, 22,  0,  0, 22 },
  87.     { 44,  88,  88, 44,  0,  0, 44 },
  88.     { 87, 174, 174, 87,  0,  0, 87 }
  89. };
  90. short bsy[NUMPOWERS][7] = {
  91.     { 0 },
  92.     { 0 },
  93.     {  0,   0,   4,   4,   4,  0,  0 },
  94.     {  0,   0,   8,   8,   8,  0,  0 },
  95.     {  0,   6,  20,  26,  20,  6,  0 },
  96.     {  0,  11,  37,  48,  37, 11,  0 },
  97.     {  0,  21,  75,  96,  75, 21,  0 },
  98.     {  0,  44, 148, 192, 148, 44,  0 }
  99. };
  100.  
  101. /* Coords of middles of each hex border (half a connection, basically). */
  102.  
  103. short lsx[NUMPOWERS][6] = {
  104.     { 0 },
  105.     { 0 },
  106.     {  1,  2,   1,  -1,  -2,  -1 },
  107.     {  2,  4,   2,  -2,  -4,  -2 },
  108.     {  6, 12,   6,  -6, -12,  -6 },
  109.     { 11, 22,  11, -11, -22, -11 },
  110.     { 22, 44,  22, -22, -44, -22 },
  111.     { 44, 87,  44, -44, -87, -44 }
  112. };
  113. short lsy[NUMPOWERS][6] = {
  114.     { 0 },
  115.     { 0 },
  116.     {  -2,  0,   2,   2,   0,  -2 },
  117.     {  -4,  0,   4,   4,   0,  -4 },
  118.     {  -9,  0,   9,   9,   0,  -9 },
  119.     { -18,  0,  18,  18,   0, -18 },
  120.     { -36,  0,  36,  36,   0, -36 },
  121.     { -74,  0,  74,  74,   0, -74 }
  122. };
  123.  
  124. short qx[NUMPOWERS][7], qy[NUMPOWERS][7];
  125.  
  126. static int q_computed;
  127.  
  128. /* The traditional direction characters. */
  129.  
  130. char *dirchars = "ulnbhy";
  131.  
  132. /* The image family for regions that are not yet discovered. */
  133.  
  134. ImageFamily *unseen_image = NULL;
  135.  
  136. char *unitchars = NULL;
  137. char *terrchars = NULL;
  138. char unseen_char_1;
  139. char unseen_char_2;
  140.  
  141. ImageFamily **recorded_imfs;
  142.  
  143. int num_recorded_imfs;
  144.  
  145. int max_recorded_imfs;
  146.  
  147. /* This routine finds and lists all the games that should be listed as choices for
  148.    the user. */
  149.  
  150. /* Note that we don't actually scan library folders looking for all possible game
  151.    designs therein. */
  152.  
  153. Module **possible_games = NULL;
  154.  
  155. int numgames = 0;
  156.  
  157. /* The comparison function for the game list puts un-formally-named
  158.    modules at the end, plus the default sorting puts initial-lowercased
  159.    names after uppercased ones. */
  160.  
  161. static int
  162. module_name_compare(a1, a2)
  163. CONST void *a1, *a2;
  164. {
  165.     Module *mp1, *mp2;
  166.     int rslt;
  167.  
  168.     mp1 = *((Module **) a1);
  169.     mp2 = *((Module **) a2);
  170.     if (mp1->basemodulename == NULL) {
  171.     if (mp2->basemodulename == NULL) {
  172.         /* Modules must always have a non-NULL name. */
  173.         return strcmp(mp1->name, mp2->name);
  174.     } else {
  175.         rslt = strcmp(mp1->name, mp2->basemodulename);
  176.         if (rslt == 0)
  177.           rslt = -1;
  178.         return rslt;
  179.     }
  180.     } else {
  181.     if (mp2->basemodulename == NULL) {
  182.         rslt = strcmp(mp1->basemodulename, mp2->name);
  183.         if (rslt == 0)
  184.           rslt = 1;
  185.         return rslt;
  186.     } else {
  187.         rslt = strcmp(mp1->basemodulename, mp2->basemodulename);
  188.         if (rslt != 0)
  189.           return rslt;
  190.         if (mp1->title == NULL) {
  191.         if (mp2->title == NULL) {
  192.             /* Modules must always have a non-NULL name. */
  193.             return strcmp(mp1->name, mp2->name);
  194.         } else {
  195.             return 1;
  196.         }
  197.         } else {
  198.         if (mp2->title == NULL) {
  199.             return (-1);
  200.         } else {
  201.             return strcmp(mp1->title, mp2->title);
  202.         }
  203.         }
  204.     }
  205.     }
  206. }
  207.  
  208. static int max_possible_games;
  209.  
  210. void
  211. collect_possible_games()
  212. {
  213.     int len, numresources;
  214.     char *modulename = NULL, *modulecontents = NULL;
  215.     Obj *lis;
  216.     Module *module, *basemodule;
  217.     FILE *fp;
  218.     int startline = 0, endline = 0;
  219.  
  220.     if (numgames == 0 && numutypes == 0 /* !game_already_loaded() */) {
  221.     len = numresources = 0;
  222.     lis = lispnil;
  223.     fp = open_library_file("game.dir");
  224.     if (fp != NULL) {
  225.         lis = read_form(fp, &startline, &endline);
  226.         if (consp(lis)) {
  227.         len = length(lis);
  228.         } else {
  229.         init_warning("Game directory has bad format, no games found");
  230.         }
  231.         fclose(fp);
  232.     }
  233. #ifdef MAC
  234.     numresources = CountResources('XCgm');
  235. #endif /* MAC */
  236.     max_possible_games = 2 + (len + numresources) * 2;
  237.     /* Make enough room to record all the possible games. */
  238.     possible_games =
  239.       (Module **) xmalloc(max_possible_games * sizeof(Module *));
  240.     /* Collect the intro and standard game modules and put at head
  241.        of list. */
  242.     module = get_game_module(first_game_name);
  243.     add_to_possible_games(module);
  244.     module = get_game_module(second_game_name);
  245.     add_to_possible_games(module);
  246.     /* Pick up game modules that are specified as resources. */
  247.     for (; lis != lispnil; lis = cdr(lis)) {
  248.         if (!(symbolp(car(lis)) || stringp(car(lis)))) {
  249.         init_warning("Bad name in game dir list, ignoring");
  250.         continue;
  251.         }
  252.         modulename = c_string(car(lis));
  253.         if (modulename != NULL) {
  254.         module = get_game_module(modulename);
  255.         module->contents = modulecontents;
  256.         add_to_possible_games(module);
  257.         if (module->basemodulename != NULL) {
  258.             basemodule = get_game_module(module->basemodulename);
  259.             add_to_possible_games(basemodule);
  260.         }
  261.         }
  262.     }
  263. #ifdef MAC
  264.     {
  265.       int i;
  266.       Handle modulehandle;
  267.       short moduleid;
  268.       ResType restype;
  269.       Str255 resname;
  270.  
  271.       /* Pick up game modules that are specified as resources. */
  272.       for (i = 0; i < numresources; ++i) {
  273.         modulehandle = GetIndResource('XCgm', i + 1);
  274.         /* (should test for resource validity?) */
  275.         if (0 /* size > 0 */) {
  276.         /* set modulecontents from resource */
  277.         modulecontents = NULL;
  278.         }
  279.         /* Try to pick up module name from its resource name, otherwise
  280.            assume its name in its content. */
  281.         GetResInfo(modulehandle, &moduleid, &restype, resname);
  282.         if (resname[0] > 0) {
  283.         resname[resname[0]+1] = '\0';
  284.         modulename = copy_string((char *) resname+1);
  285.         } else {
  286.         modulename = NULL;
  287.         }
  288.         if (modulename != NULL) {
  289.         module = get_game_module(modulename);
  290.         module->contents = modulecontents;
  291.         add_to_possible_games(module);
  292.         if (module->basemodulename != NULL) {
  293.             basemodule = get_game_module(module->basemodulename);
  294.             add_to_possible_games(basemodule);
  295.         }
  296.         }
  297.       }
  298.         }
  299. #endif /* MAC */
  300.     if (numgames > 3) {
  301.         /* Sort all but the first two games into alphabetical order
  302.            by displayed name. */
  303.         qsort(&(possible_games[2]), numgames - 2, sizeof(Module *),
  304.           module_name_compare);
  305.     }
  306.     }
  307. }
  308.  
  309. /* Load a game's description and add it to the list of games. */
  310.  
  311. void
  312. add_to_possible_games(module)
  313. Module *module;
  314. {
  315.     int i;
  316.  
  317.     if (module != NULL) {
  318.     if (load_game_description(module)) {
  319.         /* It might be that the module description supplies the real name,
  320.            and that the module already exists. (work on this) */
  321.         /* Don't add duplicate modules. */
  322.         for (i = 0; i < numgames; ++i) {
  323.         if (possible_games[i] == module)
  324.           return;
  325.         }
  326.         if (numgames < max_possible_games) {
  327.         possible_games[numgames++] = module;
  328.         }
  329.     }
  330.     }
  331. }
  332.  
  333. /* Choose and return a reasonable location for map displays to start out
  334.    centered on. */
  335.  
  336. void
  337. pick_a_focus(side, xp, yp)
  338. Side *side;
  339. int *xp, *yp;
  340. {
  341.     int tmpx, tmpy, dist, closest = area.maxdim;
  342.     Unit *unit, *closestunit = NULL;
  343.  
  344.     if (side->startx < 0 || side->starty < 0)
  345.       calc_start_xy(side);
  346.     if (side->startx < 0 || side->starty < 0) {
  347.     *xp = area.width / 2 - area.height / 4;  *yp = area.halfheight;
  348.     } else {
  349.     tmpx = side->startx;  tmpy = side->starty;
  350.     /* Rescan the units to find a closest one. */
  351.     for_all_side_units(side, unit) {
  352.         if (in_play(unit)) {
  353.         /* If already got one right there, just return. */
  354.         if (unit->x == tmpx && unit->y == tmpy) {
  355.             *xp = tmpx;  *yp = tmpy;
  356.             return;
  357.         } else {
  358.             dist = distance(unit->x, unit->y, tmpx, tmpy);
  359.             if (dist < closest) {
  360.             closest = dist;
  361.             closestunit = unit;
  362.             }
  363.         }
  364.         }
  365.     }
  366.     if (closestunit != NULL) {
  367.         /* Return the position of the unit closest to the avg position. */
  368.         *xp = closestunit->x;  *yp = closestunit->y;
  369.     } else {
  370.         *xp = tmpx;  *yp = tmpy;
  371.     }
  372.     }
  373. }
  374.  
  375. int
  376. num_active_displays()
  377. {
  378.     int n = 0;
  379.     Side *side;
  380.  
  381.     for_all_sides(side) {
  382.     if (active_display(side))
  383.       ++n;
  384.     }
  385.     return n;
  386. }
  387.  
  388. /* Collect a command name and an argument string from the given
  389.    string, discarding excess whitespace. */
  390.  
  391. void
  392. parse_long_name_command(cmdstr, namep, argp, buf)
  393. char *cmdstr, **namep, **argp, *buf;
  394. {
  395.     int j;
  396.     char *cmdname, *cmdarg;
  397.  
  398.     if (empty_string(cmdstr)) {
  399.     *namep = *argp = NULL;
  400.     return;
  401.     }
  402.     strcpy(buf, cmdstr);
  403.     /* Look for the first nonwhite char, make it start of command name. */
  404.     cmdname = buf;
  405.     while ((*cmdname == ' ' || *cmdname == '\t') && *cmdname != '\0')
  406.       ++cmdname;
  407.     /* Scan over command name, which is delimited by whitespace or end of line. */
  408.     for (j = 0; cmdname[j] != ' ' && cmdname[j] != '\t' && cmdname[j] != '\0'; ++j)
  409.       ;
  410.     /* If there's more than just the command name, extract an argument. */
  411.     if (cmdname[j] != '\0') {
  412.     cmdarg = cmdname + j + 1;
  413.     while ((*cmdarg == ' ' || *cmdname == '\t') && *cmdarg != '\0')
  414.       ++cmdarg;
  415.     if (*cmdarg == '\0')
  416.       cmdarg = NULL;
  417.     } else {
  418.     cmdarg = NULL;
  419.     }
  420.     /* Terminate the command name. */
  421.     cmdname[j] = '\0';
  422.     /* Remove trailing whitespace from the argument. */
  423.     if (!empty_string(cmdarg)) {
  424.     for (j = strlen(cmdarg) - 1; j >= 0 && (cmdarg[j] == ' ' || cmdarg[j] == '\t'); --j)
  425.       ;
  426.     cmdarg[j + 1] = '\0';
  427.     }
  428.     DGprintf("Command is \"%s\", argument is \"%s\"\n",
  429.          (cmdname != NULL ? cmdname : "<null>"),
  430.          (cmdarg != NULL ? cmdarg : "<null>"));
  431.     *namep = cmdname;
  432.     *argp = cmdarg;
  433. }
  434.  
  435. /* Compute positions at each hex corner, slightly inset. */ 
  436.  
  437. static void
  438. compute_q()
  439. {
  440.     int d, p, w;
  441.  
  442.     for (p = 0; p < NUMPOWERS; ++p) {
  443.     if (p < 2)
  444.       continue;
  445.     w = bwid[p] + 1;
  446.     for_all_directions(d) {
  447.         qx[p][d] = bsx[p][d] + ((hws[p] - 2 * bsx[p][d]) * w) / (2 * mags[p]);
  448.         qy[p][d] = bsy[p][d] + ((hhs[p] - 2 * bsy[p][d]) * w) / (2 * mags[p]);
  449.     }
  450.     qx[p][NUMDIRS] = qx[p][0];
  451.     qy[p][NUMDIRS] = qy[p][0];
  452.     }
  453. }
  454.  
  455. VP *
  456. new_vp()
  457. {
  458.     int t, thickest;
  459.     VP *vp;
  460.  
  461.     if (!q_computed) {
  462.     compute_q();
  463.     q_computed = TRUE;
  464.     }
  465.     vp = (VP *) xmalloc(sizeof(VP));
  466.     /* View at a 90 degree angle by default. */
  467.     vp->angle = 90;
  468.     /* No vertical exaggeration by default. */
  469.     vp->vertscale = 1;
  470.     vp->cellwidth = area.cellwidth;
  471.     /* If the cellwidth is not reasonable for drawing elevations, use an
  472.        approximation based on the range of elevations and terrain thicknesses. */
  473.     if (vp->cellwidth <= 1) {
  474.     thickest = 0;
  475.     for_all_terrain_types(t) {
  476.         if (t_thickness(t) > thickest)
  477.           thickest = t_thickness(t);
  478.     }
  479.     vp->cellwidth = ((area.maxelev + thickest) - area.avgelev) / 2;
  480.     }
  481.     if (vp->cellwidth < 1)
  482.       vp->cellwidth = 1;
  483.     return vp;
  484. }
  485.  
  486. /* Given a viewport and a cell, figure out where it UL corner will be. */
  487.  
  488. void
  489. xform_cell(vp, x, y, sxp, syp)
  490. VP *vp;
  491. int x, y, *sxp, *syp;
  492. {
  493.     int xnw, elev, offset;
  494.  
  495.     if (in_area(x, y)) {
  496.     /* If x is in the upper right part of the area, wrap it around to the left,
  497.        thus making it negative.  The transformed value will be positive, however. */
  498.     xnw = x;
  499.     if (area.xwrap && xnw >= (area.width - y / 2))
  500.       xnw -= area.width;
  501.     *sxp = xnw * vp->hw + (y * vp->hw) / 2 - vp->sx;
  502.     *syp = (vp->totsh - (vp->hh + y * vp->hch)) - vp->sy;
  503.     if (vp->angle != 90) {
  504.         elev = (elevations_defined() ? elev_at(x, y) : 0) - area.avgelev;
  505.         /* We see the top of the terrain in the cell, not the bottom. */
  506.         elev += t_thickness(terrain_at(x, y));
  507.         /* Exaggerate the vertical scale if requested. */
  508.         elev *= vp->vertscale;
  509.         offset = (elev * vp->hh) / vp->cellwidth;
  510.         *syp -= offset;
  511.     }
  512.     } else {
  513.     /* Always die on this, indicates bugs that must be fixed. */
  514.     run_error("attempting to xform %d,%d", x, y);
  515.     }
  516. }
  517.  
  518. void
  519. xform_unit(vp, unit, sxp, syp, swp, shp)
  520. VP *vp;
  521. Unit *unit;
  522. int *sxp, *syp, *swp, *shp;
  523. {
  524.     int num = 0, n = -1, sq, sx, sy, sx1, sy1, sw1, sh1;
  525.     int x = unit->x, y = unit->y;
  526.     Unit *unit2;
  527.  
  528.     if (unit->transport == NULL) {
  529.     xform_cell(vp, x, y, &sx, &sy);
  530.     /* Adjust to the unit box within the cell. */
  531.     sx += (vp->hw - vp->uw) / 2;  sy += (vp->hh - vp->uh) / 2;
  532.     /* Figure out our position in this cell's stack. */
  533.     for_all_stack(x, y, unit2) {
  534.         /* (should only count units visible to a given side) */
  535.         if (unit == unit2)
  536.           n = num;
  537.         ++num;
  538.     }
  539.     if (n < 0) {
  540.         run_warning("xform_unit weirdness with %s", unit_desig(unit));
  541.         *sxp = *syp = 0;
  542.         *swp = *shp = 1;
  543.         return;
  544.     }
  545.     if (num <= 1) {
  546.         sq = 1;
  547.     } else if (num <= 4) {
  548.         sq = 2;
  549.     } else if (num <= 16) {
  550.         sq = 4;
  551.     } else if (num <= 256) {
  552.         sq = 8;
  553.     } else {
  554.         /* This is room for 65,536 units in a stack. */
  555.         sq = 16;
  556.     }
  557.     *swp = vp->uw / sq;  *shp = vp->uh / sq;
  558.     *sxp = sx + *swp * (n / sq);  *syp = sy + *shp * (n % sq);
  559.     } else {
  560.     /* Go up the transport chain to get the bounds for this unit. */
  561.     xform_unit(vp, unit->transport, &sx1, &sy1, &sw1, &sh1);
  562.     xform_occupant(vp, unit->transport, unit, sx1, sy1, sw1, sh1, sxp, syp, swp, shp);
  563.     }
  564. }
  565.  
  566. void
  567. xform_unit_self(vp, unit, sxp, syp, swp, shp)
  568. VP *vp;
  569. Unit *unit;
  570. int *sxp, *syp, *swp, *shp;
  571. {
  572.     int sx1, sy1, sw1, sh1;
  573.  
  574.     if (unit->transport == NULL) {
  575.     if (unit->occupant == NULL) {
  576.         xform_unit(vp, unit, sxp, syp, swp, shp);
  577.     } else {
  578.         xform_unit(vp, unit, &sx1, &sy1, &sw1, &sh1);
  579.         xform_occupant(vp, unit, unit, sx1, sy1, sw1, sh1, sxp, syp, swp, shp);
  580.     }
  581.     } else {
  582.     xform_unit(vp, unit->transport, &sx1, &sy1, &sw1, &sh1);
  583.     xform_occupant(vp, unit->transport, unit, sx1, sy1, sw1, sh1, sxp, syp, swp, shp);
  584.     }
  585. }
  586.  
  587. void
  588. xform_occupant(vp, transport, unit, sx, sy, sw, sh, sxp, syp, swp, shp)
  589. VP *vp;
  590. Unit *transport, *unit;
  591. int sx, sy, sw, sh, *sxp, *syp, *swp, *shp;
  592. {
  593.     int num = 0, n = -1, nmx, nmy;
  594.     Unit *unit2;
  595.  
  596.     /* Figure out the position of this unit amongst all the occupants. */
  597.     for_all_occupants(transport, unit2) {
  598.     if (unit2 == unit)
  599.       n = num;
  600.     ++num;
  601.     }
  602.     if (unit == transport) {
  603.     if (num > 0) {
  604.         /* Transport image shrinks by half in each dimension. */
  605.         *swp = sw / 2;  *shp = sh / 2;
  606.     }
  607.     /* Transport is always in the UL corner. */
  608.     *sxp = sx;  *syp = sy;
  609.     } else {
  610.     if (n < 0)
  611.       run_error("xform_occupant weirdness");
  612.     /* Compute how the half-box will be subdivided.  Only use powers of two,
  613.        so image scaling works better. */
  614.     if (num <= 2) {
  615.         nmx = 2;
  616.     } else if (num <= 8) {
  617.         nmx = 4;
  618.     } else if (num <= 128) {
  619.         nmx = 8;
  620.     } else {
  621.         /* This is room for 32,768 units in a stack. */
  622.         nmy = 16;
  623.     }
  624.     nmy = nmx / 2;
  625.     *swp = sw / nmx;  *shp = (sh / 2) / nmy;
  626.     *sxp = sx + *swp * (n / nmy);  *syp = sy + sh / 2 + *shp * (n % nmy);
  627.     }
  628. }
  629.  
  630. /* Scale one viewport box to its position in another. */
  631.  
  632. void
  633. scale_vp(vp, vp2, sxp, syp, swp, shp)
  634. VP *vp, *vp2;
  635. int *sxp, *syp, *swp, *shp;
  636. {
  637.     *sxp = (vp2->sx * vp->hw) / vp2->hw - vp->sx;
  638.     *syp = (vp2->sy * vp->hch) / vp2->hch - vp->sy;
  639.     *swp = (vp2->pxw * vp->hw) / vp2->hw;
  640.     *shp = (vp2->pxh * vp->hch) / vp2->hch;
  641. }
  642.  
  643. int
  644. nearest_cell(vp, sx, sy, xp, yp)
  645. VP *vp;
  646. int sx, sy, *xp, *yp;
  647. {
  648.     /* Flip the raw y and then scale to hex coords. */
  649.     *yp = (vp->totsh - (vp->sy + sy)) / vp->hch;
  650.     /* Scale adjusted x to hex coord. */
  651.     *xp = (sx + vp->sx - (*yp * vp->hw) / 2);
  652.     /* The division by hw below might round towards 0, so wrap negative numbers
  653.        around to positive values.  This should only ever happens for cylinder
  654.        areas, but doesn't hurt to just adjust all negative values. */
  655.     if (*xp < 0)
  656.       *xp += (2 * area.width * vp->hw);
  657.     *xp = *xp / vp->hw;
  658.     /* If the magnification of the map is large enough that the top and bottom
  659.        edges of a hex are visibly sloping, then we have to take those edges
  660.        int account, and accurately. */
  661.     if ((vp->hh - vp->hch) / 2 > 1) {
  662.     /* (should adjust according to hex boundaries correctly here) */
  663.     }
  664.     /* Wrap coords as usual. */
  665.     if (area.xwrap)
  666.       *xp = wrapx(*xp);
  667.     DGprintf("Pixel %d,%d -> hex %d,%d\n", sx, sy, *xp, *yp);
  668.     return (in_area(*xp, *yp));
  669. }
  670.  
  671. int
  672. nearest_boundary(vp, sx, sy, xp, yp, dirp)
  673. VP *vp;
  674. int sx, sy, *xp, *yp, *dirp;
  675. {
  676.     int sx2, sy2, ydelta, hexslope;
  677.  
  678.     /* Get the nearest cell... */
  679.     if (nearest_cell(vp, sx, sy, xp, yp)) {
  680.     /* ... and xform it back to get the pixel coords. */ 
  681.     xform_cell(vp, *xp, *yp, &sx2, &sy2);
  682.     ydelta = sy - sy2;
  683.     hexslope = (vp->hh - vp->hch) / 2;
  684.     if (sx - sx2 > vp->hw / 2) {
  685.         *dirp = ((ydelta < hexslope) ? NORTHEAST : (ydelta > vp->hch ? SOUTHEAST : EAST));
  686.     } else {
  687.         *dirp = ((ydelta < hexslope) ? NORTHWEST : (ydelta > vp->hch ? SOUTHWEST : WEST));
  688.     }
  689.     DGprintf("Pixel %d,%d -> hex %d,%d dir %d\n", sx, sy, *xp, *yp, *dirp);
  690.     return TRUE;
  691.     } else {
  692.     return FALSE;
  693.     }
  694. }
  695.  
  696. Unit *
  697. find_unit_or_occ(vp, unit, usx, usy, usw, ush, sx, sy)
  698. VP *vp;
  699. Unit *unit;
  700. int usx, usy, usw, ush, sx, sy;
  701. {
  702.     int usx1, usy1, usw1, ush1;
  703.     Unit *occ, *rslt;
  704.  
  705.     /* See if the point might be over an occupant. */
  706.     if (unit->occupant != NULL) {
  707.     for_all_occupants(unit, occ) {
  708.         xform_unit(vp, occ, &usx1, &usy1, &usw1, &ush1);
  709.         rslt = find_unit_or_occ(vp, occ, usx1, usy1, usw1, ush1, sx, sy);
  710.         if (rslt)
  711.           return rslt;
  712.     }
  713.     }
  714.     /* Otherwise see if it could be the unit itself.  This has the effect of
  715.        "giving" the transport everything in its box that is not in an occ. */
  716.     xform_unit(vp, unit, &usx1, &usy1, &usw1, &ush1);
  717.     if (between(usx1, sx, usx1 + usw1) && between(usy1, sy, usy1 + ush1))
  718.       return unit;
  719.     return NULL;
  720. }
  721.  
  722. Unit *
  723. find_unit_at(vp, x, y, sx, sy)
  724. VP *vp;
  725. int x, y, sx, sy;
  726. {
  727.     int usx, usy, usw, ush;
  728.     Unit *unit, *rslt;
  729.     
  730.     for_all_stack(x, y, unit) {
  731.     xform_unit(vp, unit, &usx, &usy, &usw, &ush);
  732.     rslt = find_unit_or_occ(vp, unit, usx, usy, usw, ush, sx, sy);
  733.     if (rslt)
  734.       return rslt;
  735.     }
  736.     return NULL;
  737. }
  738.  
  739. int
  740. nearest_unit(vp, sx, sy, unitp)
  741. VP *vp;
  742. int sx, sy;
  743. Unit **unitp;
  744. {
  745.     int x, y;
  746.     
  747.     if (!nearest_cell(vp, sx, sy, &x, &y)) {
  748.     *unitp = NULL;
  749.     DGprintf("Pixel %d,%d -> outside area\n", sx, sy);
  750.     return FALSE;
  751.     }
  752.     if (vp->power > 4) {
  753.     *unitp = find_unit_at(vp, x, y, sx, sy);
  754.     } else {
  755.     *unitp = unit_at(x, y);
  756.     }
  757.     DGprintf("Pixel %d,%d -> unit %s\n", sx, sy, unit_desig(*unitp));
  758.     return TRUE;
  759. }
  760.  
  761. int
  762. cell_is_visible(vp, x, y)
  763. VP *vp;
  764. int x, y;
  765. {
  766.     int sx, sy;
  767.  
  768.     if (!in_area(x, y))
  769.       return FALSE;   
  770.     xform_cell(vp, x, y, &sx, &sy);
  771.     if (area.xwrap && sx > vp->totsw)
  772.       sx -= vp->totsw;
  773.     if (sx + vp->hw < 0)
  774.       return FALSE;
  775.     if (sx > vp->pxw) 
  776.       return FALSE;
  777.     if (sy + vp->hh < 0)
  778.       return FALSE;
  779.     if (sy > vp->pxh)
  780.       return FALSE;
  781.     return TRUE;
  782. }
  783.  
  784. /* Decide whether given location is away from the edge of the map's window. */
  785.  
  786. int
  787. cell_is_in_middle(vp, x, y)
  788. VP *vp;
  789. int x, y;
  790. {
  791.     int sx, sy, insetx1, insety1, insetx2, insety2;
  792.     
  793.     if (!in_area(x, y))
  794.       return FALSE;   
  795.     xform_cell(vp, x, y, &sx, &sy);
  796.     /* Adjust to be the center of the cell, more reasonable if large. */
  797.     sx += vp->hw / 2;  sy += vp->hh / 2;
  798.     insetx1 = min(vp->pxw / 4, 1 * vp->hw);
  799.     insety1 = min(vp->pxh / 4, 1 * vp->hch);
  800.     insetx2 = min(vp->pxw / 4, 2 * vp->hw);
  801.     insety2 = min(vp->pxh / 4, 2 * vp->hch);
  802.     if (sx < insetx2)
  803.       return FALSE;
  804.     if (sx > vp->pxw - insetx2)
  805.       return FALSE;
  806.     if (sy < (between(2, y, area.height-3) ? insety2 : insety1))
  807.       return FALSE;
  808.     if (sy > vp->pxh - (between(2, y, area.height-3) ? insety2 : insety1))
  809.       return FALSE;
  810.     return TRUE;
  811. }
  812.  
  813. /* Set vcx/vcy to point to the center of the view. */
  814.  
  815. void
  816. focus_on_center(vp)
  817. VP *vp;
  818. {
  819.     vp->vcy = (vp->totsh - (vp->sy + vp->pxh / 2)) / vp->hch;
  820.     vp->vcx = vp->sx / vp->hw - (vp->vcy / 2) + (vp->pxw / vp->hch) / 2;
  821.     /* Restrict the focus to be *inside* the area. */
  822.     vp->vcy = max(1, min(vp->vcy, area.height - 2));
  823.     if (area.xwrap) {
  824.     vp->vcx = wrapx(vp->vcx);
  825.     } else {
  826.     vp->vcx = max(1, min(vp->vcx, area.width - 2));
  827.     if (vp->vcx + vp->vcy < area.halfheight + 1)
  828.       vp->vcx = area.halfheight + 1;
  829.     if (vp->vcx + vp->vcy > area.width + area.halfheight - 1)
  830.       vp->vcx = area.width + area.halfheight - 1;
  831.     }
  832. }
  833.  
  834. void
  835. center_on_focus(vp)
  836. VP *vp;
  837. {
  838.     int xnw = vp->vcx;
  839.     if (area.xwrap && xnw >= (area.width - vp->vcy / 2))
  840.       xnw -= area.width;
  841.  
  842.     /* Scale, add hex offset adjustment, translate to get left edge. */
  843.     vp->sx = xnw * vp->hw + (vp->vcy * vp->hw) / 2 - vp->pxw / 2 + vp->hw / 2;
  844.     /* Scale, translate to top edge, flip. */
  845.     vp->sy = vp->totsh - (vp->vcy * vp->hch + vp->pxh / 2 + vp->hh / 2);
  846.     /* Weird vcx,vcy might make sx,sy nonsensical, so clip to rational
  847.        limits. */
  848.     vp->sx = max(hexagon_adjust(vp), min(vp->sx, vp->totsw - vp->pxw));
  849.     vp->sy = max(0, min(vp->sy, vp->totsh - vp->pxh));
  850.     DGprintf("View at %d,%d, focused at %d,%d\n",
  851.          vp->sx, vp->sy, vp->vcx, vp->vcy);
  852. }
  853.  
  854. int
  855. set_view_size(vp, w, h)
  856. VP *vp;
  857. int w, h;
  858. {
  859.     /* (should limit these values) */
  860.     vp->pxw = w;  vp->pxh = h;
  861.     return TRUE;
  862. }
  863.  
  864. int
  865. set_view_position(vp, sx, sy)
  866. VP *vp;
  867. int sx, sy;
  868. {
  869.     /* (should limit these values) */
  870.     vp->sx = sx;  vp->sy = sy;
  871.     /* Clip to rational limits. */
  872.     vp->sx = max(hexagon_adjust(vp), min(vp->sx, vp->totsw - vp->pxw));
  873.     vp->sy = max(0, min(vp->sy, vp->totsh - vp->pxh));
  874.     return TRUE;
  875. }
  876.  
  877. /* Given a magnification power, calculate and cache the sizes within a cell,
  878.    and the scaled size in pixels of the entire world. */
  879.  
  880. int
  881. set_view_power(vp, power)
  882. VP *vp;
  883. int power;
  884. {
  885.     vp->power = power;
  886.     vp->mag = mags[power]; /* is this used?? */
  887.     vp->hw = hws[power];  vp->hh = hhs[power];
  888.     vp->hch = hcs[power];
  889.     vp->uw = uws[power];  vp->uh = uhs[power];
  890.     if (vp->angle == 30) {
  891.     vp->hh /= 2;
  892.     vp->hch /= 2;
  893.     } else if (vp->angle == 15) {
  894.     vp->hh /= 4;
  895.     vp->hch /= 4;
  896.     }
  897.     /* Calculate and cache the width in pixels of the whole area, adding an
  898.        an adjustment to account for the "bulge" of hexagon-shaped areas. */
  899.     vp->totsw = area.width * vp->hw + hexagon_adjust(vp);
  900.     /* Total scaled height is based on center-to-center height, plus an adjustment
  901.        to include the bottom parts of the bottom row. */
  902.     vp->totsh = area.height * vp->hch + (vp->hh - vp->hch);
  903.     DGprintf("Power is now %d, total scaled area is %d x %d\n",
  904.          vp->power, vp->totsw, vp->totsh);
  905.     return TRUE;
  906. }
  907.  
  908. int
  909. set_view_focus(vp, x, y)
  910. VP *vp;
  911. int x, y;
  912. {
  913.     vp->vcx = x;  vp->vcy = y;
  914.     return TRUE;
  915. }
  916.  
  917. int
  918. set_view_angle(vp, angle)
  919. VP *vp;
  920. int angle;
  921. {
  922.     if (!(angle == 90 || angle == 30 || angle == 15)) {
  923.     run_warning("Bad angle %d, setting to 90", angle);
  924.     angle = 90;
  925.     }
  926.     vp->angle = angle;
  927.     vp->hh = hhs[vp->power];
  928.     vp->hch = hcs[vp->power];
  929.     vp->uh = uhs[vp->power];
  930.     if (vp->angle == 30) {
  931.     vp->hh /= 2;
  932.     vp->hch /= 2;
  933.     vp->uh /= 2;
  934.     } else if (vp->angle == 15) {
  935.     vp->hh /= 4;
  936.     vp->hch /= 4;
  937.     vp->uh /= 4;
  938.     }
  939.     /* Total scaled height is based on center-to-center height, plus an
  940.        adjustment to include the bottom parts of the bottom row. */
  941.     vp->totsh = area.height * vp->hch + (vp->hh - vp->hch);
  942.     DGprintf("Angle is now %d, total scaled area is %d x %d\n",
  943.          vp->angle, vp->totsw, vp->totsh);
  944.     return TRUE;
  945. }
  946.  
  947. int
  948. set_view_direction(vp, dir)
  949. VP *vp;
  950. int dir;
  951. {
  952.     return TRUE;
  953. }
  954.  
  955. void
  956. compute_fire_line_segment(sx1, sy1, sx2, sy2, i, n, xx, yy, dx, dy)
  957. int sx1, sy1, sx2, sy2, i, n, *xx, *yy, *dx, *dy;
  958. {
  959.     /* Position one segment of a line between the locations. */
  960.     *dx = (sx2 - sx1) / n;  *dy = (sy2 - sy1) / n;
  961.     *xx = sx1 + ((i / 2) % n) * *dx;  *yy = sy1 + ((i / 2) % n) * *dy;
  962. }
  963.  
  964. /* This routine can be used by the interface to place legends */
  965.  
  966. /* orient==0 :  E (horizontal) only;
  967.    orient==1 :  E, SE, NE;
  968.    orient==2 :  E, SE, NE, ESE, ENE, N; */
  969.  
  970. /* block==0  :  write over any unit;
  971.    block==1  :  don't write over "city-like" units;
  972.    block==2  :  don't write over visible units. */
  973.  
  974. extern void
  975. place_feature_legends(leg, nf, side, orient, block)
  976. Legend *leg;
  977. int nf;
  978. Side *side;
  979. int orient, block;
  980. {
  981.     int x, y, x1, y1, dx, dy, f, i, i3, id, d, nd, d1, dc;
  982.     double dist;
  983.     static int ndt[] = { 1, 3, 6 },
  984.     dt[] = { EAST, SOUTHEAST, NORTHEAST, NORTHEAST, SOUTHEAST, EAST },
  985.     da[] = { 0, -60, 60, 90, -30, 30 };
  986.     unsigned char *auxf_layer, dmask;
  987.     
  988.     if (!features_defined())
  989.       return;
  990.     
  991.     orient = min(orient, 2);
  992.     nd = ndt[orient];
  993.     
  994.     for (f = 1; f <= nf; f++) {
  995.     leg[f-1].ox = 0;
  996.     leg[f-1].oy = 0;
  997.     leg[f-1].dx = 0;
  998.     leg[f-1].dy = 0;
  999.     leg[f-1].angle = 0;
  1000.     leg[f-1].dist  = -1;
  1001.     }
  1002.     
  1003.     /* Speedup: in auxf_layer we keep this information:
  1004.        the cell is unseen or hosts a blocking unit (bit 7);
  1005.        the cell has already been reached from direction id (bit id)
  1006.        [this avoids repeating the same path over and over;
  1007.     note that directions 3,4,5 zig-zag with step 3,
  1008.     so this bit is set/checked only every 3 steps.] */
  1009.     
  1010.     auxf_layer = (unsigned char *)
  1011.       malloc(area.height * area.width * sizeof(unsigned char));
  1012.     
  1013.     if (!auxf_layer)
  1014.       return;
  1015.     
  1016.     for_all_cells(x, y) {
  1017.     if (terrain_seen_at(side, x, y) == NONTTYPE ||
  1018.         blocking_utype(utype_seen_at(side, x, y), block)) {
  1019.         aset(auxf_layer, x, y, '\200');
  1020.     } else {
  1021.         aset(auxf_layer, x, y, '\0');
  1022.     }
  1023.     }
  1024.     
  1025.     for_all_cells(x, y) {
  1026.     f = raw_feature_at(x, y);
  1027.     if (f < 1 || f > nf)
  1028.       continue;
  1029.  
  1030.     for (id = 0; id < nd; id++) {
  1031.         dmask = '\001' << id;
  1032.         d = dt[id];
  1033.         d1 = ((id < 3) ? d : left_dir(left_dir(d)));
  1034.         x1 = x;  y1 = y;
  1035.         dx = dy = 0;
  1036.         i3 = i = 0;
  1037.         dist = 0;
  1038.         while (raw_feature_at(x1, y1) == f &&
  1039.            !(aref(auxf_layer, x1, y1) &
  1040.              ((id < 3 || !i3) ? ('\200' | dmask) : '\200'))) {
  1041.         if (dist > leg[f-1].dist && (id < 3 || !i3)) {
  1042.             leg[f-1].ox =  x;  leg[f-1].oy =  y;
  1043.             leg[f-1].dx = dx;  leg[f-1].dy = dy;
  1044.             leg[f-1].angle = da[id];
  1045.             leg[f-1].dist  = dist;
  1046.         }
  1047.         if (id < 3 || !i3) {
  1048.             auxf_layer[area.width * y1 + x1] |= dmask;
  1049.         }
  1050.         dc = ((i3 == 1) ? d1 : d);
  1051.         dx += dirx[dc];
  1052.         x1 = wrapx(x1 + dirx[dc]);
  1053.         dy += diry[dc];
  1054.         y1 += diry[dc];
  1055.         dist += ((id < 3) ? 1.0 : (i3 ? 0.5 * 1.73205080756888 : 0.0));
  1056.         ++i;
  1057.         i3 = i % 3;
  1058.         }
  1059.     }
  1060.     }
  1061.     
  1062.     free(auxf_layer);
  1063. }
  1064.  
  1065. static int
  1066. blocking_utype(u, block)
  1067. int u, block;
  1068. {
  1069.     if (u == NONUTYPE || block == 0)
  1070.       return 0;
  1071.     if (block > 1)
  1072.       return 1;
  1073.     /* block==1:  only visible see-always unmovable units */
  1074.     return ((u_already_seen(u) > 99 || u_see_always(u)) && !mobile(u));
  1075. }
  1076.  
  1077.  
  1078. /* (needs a better home?) */
  1079.  
  1080. /* Given a side and a unit, calculate the correct "next unit".  Typically
  1081.    used by autonext options, thus the name. */
  1082.  
  1083. Unit *
  1084. autonext_unit(side, unit)
  1085. Side *side;
  1086. Unit *unit;
  1087. {
  1088.     int i, uniti = -1, n;
  1089.     Unit *nextunit;
  1090.  
  1091.     if (!side->ingame
  1092.     || side->finishedturn
  1093.     || side->actionvector == NULL)
  1094.       return NULL;
  1095.     if (could_be_next_unit(unit) && side_controls_unit(side, unit))
  1096.       return unit;
  1097.     for (i = 0; i < side->actionvector->numunits; ++i) {
  1098.         nextunit = (side->actionvector->units)[i].unit;
  1099.         if (in_play(nextunit) && side_controls_unit(side, nextunit)) {
  1100.         if (unit == NULL || unit == nextunit) {
  1101.         uniti = i;
  1102.         break;
  1103.         }
  1104.         }
  1105.     }
  1106.     if (uniti < 0)
  1107.       return NULL;
  1108.     /* (should scan for both a preferred and an alternate - preferred
  1109.        could be within a supplied bbox so as to avoid scrolling) */
  1110.     for (i = uniti; i < uniti + side->actionvector->numunits; ++i) {
  1111.         n = i % side->actionvector->numunits;
  1112.         nextunit = (side->actionvector->units)[n].unit;
  1113.         if (could_be_next_unit(nextunit) && side_controls_unit(side, nextunit))
  1114.           return nextunit;
  1115.     }
  1116.     return NULL;
  1117. }
  1118.  
  1119. #if 0
  1120. int
  1121. in_box(x, y, lx, ly, w, h)
  1122. int x, y, lx, ly, w, h;
  1123. {
  1124.     if ( !between(ly, y, ly+h) )
  1125.       return FALSE;
  1126.     lx -= (y - ly) / 2;
  1127.     return between(lx, x, lx+w);
  1128. }
  1129. #endif
  1130.  
  1131. /*
  1132.  * This should really be called autonext_unit and the decision
  1133.  * whether to check inbox or not should depend on the bbox being
  1134.  * valid. i.e. could be called with -1,-1,-1,-1 to disable the bbox.
  1135.  */
  1136. Unit *
  1137. autonext_unit_inbox(side, unit, vp)
  1138. Side *side;
  1139. Unit *unit;
  1140. VP *vp;
  1141. {
  1142.     int i, u, mx, my, val, prefval = -999, v = 10;
  1143.     Unit *nextunit = NULL, *prefunit = NULL;
  1144.  
  1145.     if (!side->ingame || side->finishedturn || side->actionvector == NULL)
  1146.       return NULL;
  1147.  
  1148.     /* degenerate case... this unit still has stuff to do. */
  1149.     if (could_be_next_unit(unit) && side_controls_unit(side, unit))
  1150.     return unit;
  1151.  
  1152.     if (unit == NULL) {
  1153.     u = 0;
  1154.     if (!nearest_cell(vp, vp->sx + vp->pxw / 2, vp->sy + vp->pxh / 2, &mx, &my)) {
  1155.         mx = area.width / 2;  my = area.halfheight;
  1156.     }
  1157.     } else {
  1158.     u = unit->type;
  1159.     mx = unit->x;  my = unit->y;
  1160.     }
  1161.  
  1162.     for (i = 0; i < side->actionvector->numunits; ++i) {
  1163.         nextunit = (side->actionvector->units)[i].unit;
  1164.     if (side_controls_unit(side, nextunit) && could_be_next_unit(nextunit)) {
  1165.         val = v - distance(nextunit->x, nextunit->y, mx, my);
  1166.         if (cell_is_in_middle(vp, nextunit->x, nextunit->y))
  1167.           val += v;
  1168.         if (nextunit->type == u)
  1169.           val += 2;
  1170.  
  1171.         if (val > prefval) {
  1172.         prefval = val;
  1173.         prefunit = nextunit;
  1174.         }
  1175.     }
  1176.     }
  1177.     return prefunit;
  1178. }
  1179.  
  1180. int
  1181. could_be_next_unit(unit)
  1182. Unit *unit;
  1183. {
  1184.     return (unit != NULL
  1185.         && alive(unit)
  1186.         && inside_area(unit->x, unit->y)
  1187.         && (unit->act
  1188.         && unit->act->acp > 0 /*
  1189.         && !has_pending_action(unit) */)
  1190.         && (unit->plan
  1191.         && !unit->plan->asleep
  1192.         && !unit->plan->reserve
  1193.         && !unit->plan->delayed
  1194.         && unit->plan->waitingfortasks));
  1195. }
  1196.  
  1197. Unit *
  1198. find_next_occupant(unit)
  1199. Unit *unit;
  1200. {
  1201.     Unit *nextup;
  1202.  
  1203.     if (unit->occupant != NULL) {
  1204.     return unit->occupant;
  1205.     } else if (unit->nexthere != NULL) {
  1206.     return unit->nexthere;
  1207.     } else {
  1208.     nextup = unit->transport;
  1209.     if (nextup != NULL) {
  1210.         while (nextup->transport != NULL && nextup->nexthere == NULL) {
  1211.         nextup = nextup->transport;
  1212.         }
  1213.         if (nextup->nexthere != NULL)
  1214.           return nextup->nexthere;
  1215.         if (nextup->transport == NULL)
  1216.           return nextup;
  1217.     } else {
  1218.         /* This is a no-op if there is no stacking within a hex. */
  1219.         return unit_at(unit->x, unit->y);
  1220.     }
  1221.     }
  1222.     return unit;
  1223. }
  1224.  
  1225. /* Given a character, compute the direction(s) that it represents.
  1226.    Return the number of directions. */
  1227.  
  1228. int
  1229. char_to_dir(ch, dir1p, dir2p, modp)
  1230. int ch, *dir1p, *dir2p, *modp;
  1231. {
  1232.     char basech, *rawdir;
  1233.     int ndirs = 0;
  1234.  
  1235.     if (isupper(ch)) {
  1236.     basech = tolower(ch);
  1237.     if (modp)
  1238.      *modp = 1;
  1239.     } else if (ch < ' ') {
  1240.     basech = ch + 0x60;
  1241.     if (modp)
  1242.       *modp = 2;
  1243.     } else {
  1244.     basech = ch;
  1245.     if (modp)
  1246.       *modp = 0;
  1247.     }
  1248.     rawdir = strchr(dirchars, basech);
  1249.     if (rawdir) {
  1250.     *dir1p = rawdir - dirchars;
  1251.     ndirs = 1;
  1252.     } else if (basech == 'k') {
  1253.     if (flip_coin()) {
  1254.         *dir1p = NORTHEAST;
  1255.         if (dir2p)
  1256.           *dir2p = NORTHWEST;
  1257.     } else {
  1258.         *dir1p = NORTHWEST;
  1259.         if (dir2p)
  1260.           *dir2p = NORTHEAST;
  1261.     }
  1262.     ndirs = 2;
  1263.     } else if (basech == 'j') {
  1264.     if (flip_coin()) {
  1265.         *dir1p = SOUTHEAST;
  1266.         if (dir2p)
  1267.           *dir2p = SOUTHWEST;
  1268.     } else {
  1269.         *dir1p = SOUTHWEST;
  1270.         if (dir2p)
  1271.           *dir2p = SOUTHEAST;
  1272.     }
  1273.     ndirs = 2;
  1274.     }
  1275.     return ndirs;
  1276. }
  1277.  
  1278. /* Given that the player desires to move the given unit into the given
  1279.    cell/other unit, prepare a "most appropriate" action. */
  1280. /* (should share diff cell and same cell interaction code) */
  1281.  
  1282. int
  1283. advance_into_cell(side, unit, x, y, other)
  1284. Side *side;
  1285. Unit *unit, *other;
  1286. int x, y;
  1287. {
  1288. #ifdef DESIGNERS
  1289.     /* Designers use this function to push units around, bound only by the
  1290.        limits on occupancy. */
  1291.     if (side->designer) {
  1292.     return designer_teleport(unit, x, y, other);
  1293.     }
  1294. #endif /* DESIGNERS */
  1295.     if (x != unit->x || y != unit->y) {
  1296.     if (unit->act && unit->plan) { /* (should be more sophisticated test?) */
  1297.         if (distance(unit->x, unit->y, x, y) == 1) {
  1298.         if (other == NULL) {
  1299.             if (can_occupy_cell(unit, x, y)
  1300.             && valid(check_move_action(unit, unit, x, y, unit->z))) {
  1301.             prep_move_action(unit, unit, x, y, unit->z);
  1302.             return TRUE;
  1303.             } else {
  1304.             return FALSE;
  1305.             }
  1306.         }
  1307.         if (unit_trusts_unit(unit, other)) {
  1308.             /* A friend, maybe get on it. */
  1309.             if (can_occupy(unit, other)) {
  1310.             if (valid(check_enter_action(unit, unit, other))) {
  1311.                 prep_enter_action(unit, unit, other);
  1312.             } else {
  1313.                 /* (should schedule for next turn?) */
  1314.             }
  1315.             } else if (can_occupy(other, unit)) {
  1316.             if (u_acp(other->type) > 0) {
  1317.                 /* Have other unit do an enter action,
  1318.                                then move. */
  1319.                 /* (not quite right, move should happen
  1320.                    after other unit is actually inside, in
  1321.                    case it fills dest) */
  1322.                 prep_enter_action(other, other, unit);
  1323.                 set_move_to_task(unit, x, y);
  1324.             } else {
  1325.                 prep_enter_action(unit, other, unit);
  1326.                 set_move_to_task(unit, x, y);
  1327.             }
  1328.             } else if (other->transport != NULL
  1329.                    && can_occupy(unit, other->transport)) {
  1330.             if (valid(check_enter_action(unit, unit, other->transport))) {
  1331.                 prep_enter_action(unit, unit, other->transport);
  1332.             } else {
  1333.                 /* (should schedule for next turn?) */
  1334.             }
  1335.             } else if (other->transport != NULL
  1336.                    && other->transport->transport != NULL
  1337.                    && can_occupy(unit, other->transport->transport)) {
  1338.             /* two levels up should be sufficient */
  1339.             if (valid(check_enter_action(unit, unit, other->transport->transport))) {
  1340.                 prep_enter_action(unit, unit, other->transport->transport);
  1341.             } else {
  1342.                 /* (should schedule for next turn?) */
  1343.             }
  1344.             } else if (valid(check_transfer_part_action(unit, unit, unit->hp, other))) {
  1345.             prep_transfer_part_action(unit, unit, unit->hp, other);
  1346.             } else if (can_occupy_cell(unit, x, y)
  1347.                    && valid(check_move_action(unit, unit, x, y, unit->z))) {
  1348.             prep_move_action(unit, unit, x, y, unit->z);
  1349.             } else {
  1350.             return FALSE;
  1351.             }
  1352.         } else {
  1353.             /* Somebody else's unit, try to victimize it in various ways,
  1354.                trying coexistence only as a last resort. */
  1355.             if (valid(check_capture_action(unit, unit, other))) {
  1356.             prep_capture_action(unit, unit, other);
  1357.             } else if (valid(check_overrun_action(unit, unit, x, y, unit->z, 100))) {
  1358.             prep_overrun_action(unit, unit, x, y, unit->z, 100);
  1359.             } else if (valid(check_attack_action(unit, unit, other, 100))) {
  1360.             prep_attack_action(unit, unit, other, 100);
  1361.             } else if (valid(check_fire_at_action(unit, unit, other, -1))) {
  1362.             prep_fire_at_action(unit, unit, other, -1);
  1363.             } else if (valid(check_detonate_action(unit, unit, x, y, unit->z))) {
  1364.             prep_detonate_action(unit, unit, x, y, unit->z);
  1365.             } else if (valid(check_move_action(unit, unit, x, y, unit->z))) {
  1366.             prep_move_action(unit, unit, x, y, unit->z);
  1367.             } else {
  1368.             return FALSE;
  1369.             }
  1370.         }
  1371.         } else {
  1372.         /* We're not adjacent to the destination, set up a move task. */
  1373.         set_move_to_task(unit, x, y);
  1374.         }
  1375.     } else {
  1376.         /* ??? can't act ??? */
  1377.     }
  1378.     } else {
  1379.     /* Destination is in the unit's own cell. */
  1380.     if (other != NULL) {
  1381.         if (unit_trusts_unit(unit, other)) {
  1382.         if (valid(check_transfer_part_action(unit, unit, unit->hp, other))) {
  1383.             prep_transfer_part_action(unit, unit, unit->hp, other);
  1384.         } else if (valid(check_enter_action(unit, unit, other))) {
  1385.             prep_enter_action(unit, unit, other);
  1386.         } else {
  1387.             return FALSE;
  1388.         }
  1389.         } else {
  1390.         /* Somebody else's unit, try to victimize it in various ways,
  1391.            trying coexistence only as a last resort. */
  1392.         if (valid(check_capture_action(unit, unit, other))) {
  1393.             prep_capture_action(unit, unit, other);
  1394.         } else if (valid(check_attack_action(unit, unit, other, 100))) {
  1395.             prep_attack_action(unit, unit, other, 100);
  1396.         } else if (valid(check_fire_at_action(unit, unit, other, -1))) {
  1397.             prep_fire_at_action(unit, unit, other, -1);
  1398.         } else if (valid(check_detonate_action(unit, unit, x, y, unit->z))) {
  1399.             prep_detonate_action(unit, unit, x, y, unit->z);
  1400.         } else {
  1401.             return FALSE;
  1402.         }
  1403.         }
  1404.     } else if (unit->transport != NULL) {
  1405.         /* Unit is an occupant wanting to leave, but yet remain in
  1406.            the same cell as the transport. */
  1407.         if (valid(check_move_action(unit, unit, x, y, unit->z))) {
  1408.         prep_move_action(unit, unit, x, y, unit->z);
  1409.         } else {
  1410.         return FALSE;
  1411.         }
  1412.     } else {
  1413.         /* This is a no-op, don't do anything. */
  1414.     }
  1415.     }
  1416.     return TRUE;
  1417. }
  1418.  
  1419. int
  1420. give_supplies(unit, amts, rslts)
  1421. Unit *unit;
  1422. short *amts, *rslts;
  1423. {
  1424.     int m, gift, maxgift, actual, didsome;
  1425.     Unit *unit2;
  1426.  
  1427.     didsome = FALSE;
  1428.     unit2 = unit->transport;
  1429.     if (!(in_play(unit2) && completed(unit2)))
  1430.       return didsome;
  1431.     for_all_material_types(m) {
  1432.     if (rslts)
  1433.       rslts[m] = 0;
  1434.     maxgift = min(unit->supply[m], um_storage_x(unit2->type, m) - unit2->supply[m]);
  1435.     gift = ((amts == NULL || amts[m] == -1) ? (maxgift / 2) : amts[m]);
  1436.     if (gift > 0) {
  1437.         if (1 /* can do immed transfer */) {
  1438.         /* Be stingy if giver is low */
  1439.         if (2 * unit->supply[m] < um_storage_x(unit->type, m))
  1440.           gift = max(1, gift/2);
  1441.         actual = transfer_supply(unit, unit2, m, gift);
  1442.         if (rslts)
  1443.           rslts[m] = actual;
  1444.         if (actual > 0)
  1445.           didsome = TRUE;
  1446.         }
  1447.     }
  1448.     }
  1449.     return didsome;
  1450. }
  1451.  
  1452. int
  1453. take_supplies(unit, amts, rslts)
  1454. Unit *unit;
  1455. short *amts, *rslts;
  1456. {
  1457.     int m, want, actual, didsome;
  1458.     Unit *unit2;
  1459.  
  1460.     didsome = FALSE;
  1461.     for_all_material_types(m) {
  1462.     if (rslts)
  1463.       rslts[m] = 0;
  1464.     want = ((amts == NULL || amts[m] == -1)
  1465.             ? (um_storage_x(unit->type, m) - unit->supply[m])
  1466.             : amts[m]);
  1467.     if (want > 0) {
  1468.         unit2 = unit->transport;
  1469.         if (in_play(unit2) && completed(unit2)) {
  1470.         /* Be stingy if transport is low */
  1471.         if (2 * unit2->supply[m] < um_storage_x(unit2->type, m))
  1472.           want = max(1, want/2);
  1473.         actual = transfer_supply(unit2, unit, m, want);
  1474.         if (rslts)
  1475.           rslts[m] = actual;
  1476.         if (actual > 0)
  1477.           didsome = TRUE;
  1478.         }
  1479.     }
  1480.     }
  1481.     return didsome;
  1482. }
  1483.  
  1484. int
  1485. disband_unit(side, unit)
  1486. Side *side;
  1487. Unit *unit;
  1488. {
  1489.     int rslt;
  1490.  
  1491. #ifdef DESIGNERS
  1492.     if (side->designer) {
  1493.     return designer_disband(unit);
  1494.     }
  1495. #endif /* DESIGNERS */
  1496.     if (side_can_disband(side, unit)) {
  1497.     rslt = disband_unit_directly(side, unit);
  1498.     if (!completed(unit)) {
  1499.         /* Nothing complicated about getting rid of an incomplete unit. */
  1500.         kill_unit(unit, H_UNIT_DISBANDED);
  1501.     } else if (unit->plan) {
  1502.         set_disband_task(unit);
  1503.     } else {
  1504.         /* In order for this to work, we would need a way to direct one
  1505.            sort of unit to disband another.  Just fail for now. */
  1506.         return FALSE;
  1507.     }
  1508.     return TRUE;
  1509.     } else {
  1510.     return FALSE;
  1511.     }
  1512. }
  1513.  
  1514. /* Return the type to build that dialogs should highlight initially. */
  1515.  
  1516. int
  1517. favored_type(unit)
  1518. Unit *unit;
  1519. {
  1520.     int u;
  1521.  
  1522.     if (unit == NULL)
  1523.       return NONUTYPE;
  1524.     if (unit->plan
  1525.         && unit->plan->tasks
  1526.         && unit->plan->tasks->type == TASK_BUILD)
  1527.       return unit->plan->tasks->args[0];
  1528.     for_all_unit_types(u) {
  1529.         if (uu_acp_to_create(unit->type, u) > 0)
  1530.           return u;
  1531.     }
  1532.     return NONUTYPE;
  1533. }
  1534.  
  1535. /* Generic image setup. */
  1536.  
  1537. static ImageFamily *
  1538. get_generic_images(side, name, interp_hook, load_hook)
  1539. Side *side;
  1540. char *name;
  1541. void (*interp_hook) PARAMS ((ImageFamily *imf));
  1542. void (*load_hook) PARAMS ((ImageFamily *imf));
  1543. {
  1544.     FILE *fp;
  1545.     int cloned = FALSE;
  1546.     ImageFamily *imf;
  1547.  
  1548.     imf = get_imf(name);
  1549.     if (imf == NULL)
  1550.       return NULL;
  1551.     if (imf->numsizes > 0)
  1552.       if (interp_hook != NULL) {
  1553.       imf = clone_imf(imf);
  1554.       cloned = TRUE;
  1555.       (*interp_hook)(imf);
  1556.       }
  1557.     if (load_hook != NULL) {
  1558.     if (!cloned) {
  1559.         imf = clone_imf(imf);
  1560.         cloned = TRUE;
  1561.     }
  1562.     (*load_hook)(imf);
  1563.     }
  1564.     if (imf->numsizes == 0) {
  1565.     /* Collect the names/locations of all image families. */
  1566.     fp = open_library_file("imf.dir");
  1567.     if (fp != NULL) {
  1568.         load_image_families(fp, FALSE, NULL);
  1569.         fclose(fp);
  1570.     } else {
  1571.         init_warning("Cannot open \"%s\", will not use it", "imf.dir");
  1572.     }
  1573.     /* Get a (possibly empty) family. */
  1574.     imf = get_imf(name);
  1575.     if (imf == NULL)
  1576.       return NULL;
  1577.     if (imf->location != NULL) {
  1578.         /* Load data filling in the family. */
  1579.         /* (should use library path list) */
  1580.         make_pathname(xconq_libs->path, imf->location->name, "", spbuf);
  1581.         if (load_imf_file(spbuf, NULL)) {
  1582.         } else if (load_imf_file(imf->location->name, NULL)) {
  1583.         } else {
  1584.         /* complain here, or not? */
  1585.         }
  1586.         if (interp_hook != NULL) {
  1587.         imf = clone_imf(imf);
  1588.         cloned = TRUE;
  1589.         (*interp_hook)(imf);
  1590.         }
  1591.     }
  1592.     }
  1593.     return imf;
  1594. }
  1595.  
  1596. ImageFamily *
  1597. get_unit_type_images(side, u, interp_hook, load_hook, default_hook)
  1598. Side *side;
  1599. int u;
  1600. void (*interp_hook) PARAMS ((ImageFamily *imf));
  1601. void (*load_hook) PARAMS ((ImageFamily *imf));
  1602. void (*default_hook) PARAMS ((ImageFamily *imf, int n, char *name));
  1603. {
  1604.     char *name;
  1605.     ImageFamily *imf;
  1606.  
  1607.     if (!empty_string(u_image_name(u)))
  1608.       name = u_image_name(u);
  1609.     else
  1610.       name = u_internal_name(u);
  1611.     imf = get_generic_images(side, name, interp_hook, load_hook);
  1612.     if (imf != NULL && imf->numsizes == 0 && default_hook != NULL) {
  1613.     imf->ersatz = TRUE;
  1614.     (*default_hook)(imf, u, u_type_name(u));
  1615.     }
  1616.     record_imf_get(imf);
  1617.     return imf;
  1618. }
  1619.  
  1620. ImageFamily *
  1621. get_terrain_type_images(side, t, interp_hook, load_hook, default_hook)
  1622. Side *side;
  1623. int t;
  1624. void (*interp_hook) PARAMS ((ImageFamily *imf));
  1625. void (*load_hook) PARAMS ((ImageFamily *imf));
  1626. void (*default_hook) PARAMS ((ImageFamily *imf, int n, char *name));
  1627. {
  1628.     char *name;
  1629.     ImageFamily *imf;
  1630.  
  1631.     if (!empty_string(t_image_name(t)))
  1632.       name = t_image_name(t);
  1633.     else
  1634.       name = t_type_name(t);
  1635.     imf = get_generic_images(side, name, interp_hook, load_hook);
  1636.     if (imf != NULL && imf->numsizes == 0 && default_hook != NULL) {
  1637.     imf->ersatz = TRUE;
  1638.     (*default_hook)(imf, t, t_type_name(t));
  1639.     }
  1640.     record_imf_get(imf);
  1641.     return imf;
  1642. }
  1643.  
  1644. ImageFamily *
  1645. get_unseen_images(side, interp_hook, load_hook, default_hook)
  1646. Side *side;
  1647. void (*interp_hook) PARAMS ((ImageFamily *imf));
  1648. void (*load_hook) PARAMS ((ImageFamily *imf));
  1649. void (*default_hook) PARAMS ((ImageFamily *imf, char *name));
  1650. {
  1651.     if (!empty_string(g_unseen_image_name())) {
  1652.     unseen_image = get_generic_images(side, g_unseen_image_name(), interp_hook, load_hook); 
  1653.     if (unseen_image != NULL && unseen_image->numsizes == 0) {
  1654.         /* Appears to have failed - clear the unseen image then. */
  1655.         unseen_image = NULL;
  1656.         /* Note that we shouldn't try to free the imf, because it may be in use
  1657.            elsewhere. */
  1658.     }
  1659.     }
  1660.     record_imf_get(unseen_image);
  1661.     return unseen_image;
  1662. }
  1663.  
  1664. ImageFamily *
  1665. get_emblem_images(side, side2, interp_hook, load_hook, default_hook)
  1666. Side *side, *side2;
  1667. void (*interp_hook) PARAMS ((ImageFamily *imf));
  1668. void (*load_hook) PARAMS ((ImageFamily *imf));
  1669. void (*default_hook) PARAMS ((ImageFamily *imf, int n, char *name));
  1670. {
  1671.     char *name, tmpbuf[BUFSIZE];
  1672.     int s2 = side_number(side2);
  1673.     ImageFamily *imf;
  1674.  
  1675.     if (side2 != NULL && !empty_string(side2->emblemname))
  1676.       name = side2->emblemname;
  1677.     else {
  1678.     sprintf(tmpbuf, "s%d", s2);
  1679.     name = copy_string(tmpbuf);
  1680.     }
  1681.     imf = get_generic_images(side, name, interp_hook, load_hook);
  1682.     if (imf != NULL && imf->numsizes == 0 && default_hook != NULL && strcmp(name, "none") != 0) {
  1683.     (*default_hook)(imf, s2, name);
  1684.     }
  1685.     record_imf_get(imf);
  1686.     return imf;
  1687. }
  1688.  
  1689. void
  1690. record_imf_get(imf)
  1691. ImageFamily *imf;
  1692. {
  1693.     int i;
  1694.     ImageFamily **new_record;
  1695.  
  1696.     if (imf == NULL)
  1697.       return;
  1698.     /* Estimate and allocate the usual amount of space needed. */
  1699.     if (max_recorded_imfs == 0)
  1700.       max_recorded_imfs = numutypes + numttypes + (MAXSIDES + 1) + 1;
  1701.     if (recorded_imfs == NULL) {
  1702.     recorded_imfs = (ImageFamily **) xmalloc(max_recorded_imfs * sizeof(ImageFamily *));
  1703.     }
  1704.     /* Allocate more space if needed. */
  1705.     if (num_recorded_imfs >= max_recorded_imfs) {
  1706.     max_recorded_imfs += max_recorded_imfs / 2;
  1707.     new_record = (ImageFamily **) xmalloc(max_recorded_imfs * sizeof(ImageFamily *));
  1708.     for (i = 0; i < num_recorded_imfs; ++i) {
  1709.         new_record[i] = recorded_imfs[i];
  1710.     }
  1711.     recorded_imfs = new_record;
  1712.     }
  1713.     for (i = 0; i < num_recorded_imfs; ++i) {
  1714.     if (strcmp(imf->name, recorded_imfs[i]->name) == 0)
  1715.       return;
  1716.     }
  1717.     recorded_imfs[num_recorded_imfs++] = imf;
  1718.     /* Expand any interface-specific data into its all-interface form, so that
  1719.        saved games will include it.  This needs to be done now, because game
  1720.        saving may occur in a low-memory situation and there may not be enough
  1721.        memory available then. */
  1722.     make_generic_image_data(imf);
  1723. }
  1724.  
  1725. /* Compute and cache single-char representations for things. */
  1726.  
  1727. void
  1728. init_ui_chars()
  1729. {
  1730.     int u, t;
  1731.     char *str;
  1732.  
  1733.     if (unitchars == NULL) {
  1734.     unitchars = xmalloc(numutypes);
  1735.     for_all_unit_types(u) {
  1736.         str = u_uchar(u);
  1737.         unitchars[u] = (!empty_string(str) ? str[0] : utype_name_n(u, 1)[0]);
  1738.     }
  1739.     }
  1740.     if (terrchars == NULL) {
  1741.     terrchars = xmalloc(numttypes);
  1742.     for_all_terrain_types(t) {
  1743.         str = t_char(t);
  1744.         terrchars[t] = (!empty_string(str) ? str[0] : t_type_name(t)[0]);
  1745.     }
  1746.     }
  1747.     unseen_char_1 = unseen_char_2 = ' ';
  1748.     str = g_unseen_char();
  1749.     if (strlen(str) >= 1) {
  1750.     unseen_char_1 = unseen_char_2 = str[0];
  1751.     if (strlen(str) >= 2) {
  1752.         unseen_char_2 = str[1];
  1753.     }
  1754.     }
  1755. }
  1756.  
  1757. /* Write the side's view of the world, as ASCII. */
  1758.  
  1759. /* (should be intelligent enough to cut into pages, or else document
  1760.    how to do it) */
  1761. /* (maybe display names too somehow, perhaps as second layer?) */
  1762.  
  1763. #define VIEWFILE "view.ccq"
  1764.  
  1765. void
  1766. dump_text_view(side, use_both_chars)
  1767. Side *side;
  1768. int use_both_chars;
  1769. {
  1770.     char ch1, ch2;
  1771.     int x, y, t, uview, u, s, draw, i;
  1772.     Side *side2;
  1773.     Unit *unit;
  1774.     FILE *fp;
  1775.  
  1776.     fp = fopen(VIEWFILE, "w");
  1777.     if (fp != NULL) {
  1778.     for (y = area.height-1; y >= 0; --y) {
  1779.         for (i = 0; i < y; ++i)
  1780.           fputc(' ', fp);
  1781.         for (x = 0; x < area.width; ++x) {
  1782.         ch1 = ch2 = ' ';
  1783.         if (in_area(x, y) && terrain_visible(side, x, y)) {
  1784.             t = terrain_at(x, y);
  1785.             ch1 = terrchars[t];
  1786.             ch2 = (use_both_chars ? ch1 : ' ');
  1787.             draw = FALSE;
  1788.             if (units_visible(side, x, y)) {
  1789.             unit = unit_at(x, y);
  1790.             if (unit != NULL) {
  1791.                 u = unit->type;
  1792.                 s = side_number(unit->side);
  1793.                 draw = TRUE;
  1794.             }
  1795.             } else {
  1796.             uview = unit_view(side, wrapx(x), y);
  1797.             if (uview != EMPTY) {
  1798.                 u = vtype(uview);
  1799.                 s = vside(uview);
  1800.                 draw = TRUE;
  1801.             }
  1802.             }
  1803.             if (draw) {
  1804.             ch1 = unitchars[u];
  1805.             ch2 = ' ';
  1806.             if (between(1, s, 9))
  1807.               ch2 = s + '0';
  1808.             else if (s >= 10)
  1809.               /* This could get weird if s > 36, but not much
  1810.                  chance of that because MAXSIDES < 31 always. */
  1811.               ch2 = s - 10 + 'A';
  1812.             }
  1813.         }
  1814.         fputc(ch1, fp);
  1815.         fputc(ch2, fp);
  1816.         }
  1817.         fprintf(fp, "\n");
  1818.     }
  1819.     fprintf(fp, "\n\nTerrain Types:\n");
  1820.     for_all_terrain_types(t) {
  1821.         fprintf(fp, "  %c%c  %s\n", terrchars[t], terrchars[t], t_type_name(t));
  1822.     }
  1823.     fprintf(fp, "\n\nUnit Types:\n");
  1824.     for_all_unit_types(u) {
  1825.         fprintf(fp, "  %c   %s\n", unitchars[u], u_type_name(u));
  1826.     }
  1827.     fprintf(fp, "\n\nSides:\n");
  1828.     for_all_sides(side2) {
  1829.         fprintf(fp, "  %d   %s\n", side_number(side2), side_name(side2));
  1830.     }
  1831.     fclose(fp);
  1832.     notify(side, "Dumped area view to \"%s\".", VIEWFILE);
  1833.     } else {
  1834.     notify(side, "Can't open \"%s\"!!", VIEWFILE);
  1835.     }
  1836. }
  1837.  
  1838. int
  1839. terrain_seen_at(side, x, y)
  1840. Side *side;
  1841. int x, y;
  1842. {
  1843.     if (in_area(x, y)
  1844.     && (side == NULL
  1845.         || all_see_all
  1846. #ifdef DESIGNERS
  1847.         || side->designer
  1848. #endif /* DESIGNERS */
  1849.         || terrain_view(side, x, y) != UNSEEN)) {
  1850.     return terrain_at(x, y);
  1851.     } else {
  1852.     return NONTTYPE;
  1853.     }
  1854. }
  1855.  
  1856. Unit *
  1857. unit_seen_at(side, x, y)
  1858. Side *side;
  1859. int x, y;
  1860. {
  1861.     if (!in_area(x, y))
  1862.       return NULL;
  1863.     if (side == NULL
  1864.     || all_see_all
  1865. #ifdef DESIGNERS
  1866.     || side->designer
  1867. #endif /* DESIGNERS */
  1868.     || cover(side, x, y) > 0)
  1869.       return unit_at(x, y);
  1870.     return NULL;
  1871. }
  1872.  
  1873. int
  1874. utype_seen_at(side, x, y)
  1875. Side *side;
  1876. int x, y;
  1877. {
  1878.     Unit *unit;
  1879.     short view;
  1880.  
  1881.     if (!in_area(x, y))
  1882.       return NONUTYPE;
  1883.     unit = unit_seen_at(side, x, y);
  1884.     if (unit)
  1885.       return unit->type;
  1886.  
  1887.     view = unit_view(side, x, y);
  1888.     if (view != EMPTY)
  1889.       return vtype(view);
  1890.  
  1891.     return NONUTYPE;
  1892. }
  1893.  
  1894.